﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Threading;

namespace FileSharing
{
    public class DownloadTask
    {
        enum PieceStatus : byte 
        {
            Missing,
            InProgress,
            Downloaded
        }

        List<RemoteFile> Sources;
        List<TransferConnection> Connections;

        Mutex ConnectionsMut = new Mutex();
        Mutex PiecesMut = new Mutex();

        Downloads Parent;

        PieceStatus[] ReceivedPieces;

        LocalFile DestinationFile;

        public RemoteFile File { get { return Sources[0]; } }

        public float Completed
        {
            get
            {
                int i = 0;
                float result;

                PiecesMut.WaitOne();
                foreach (PieceStatus stat in ReceivedPieces)
                {
                    if (stat == PieceStatus.Downloaded)
                        i++;
                }

                result = (float)i / ReceivedPieces.Length;

                PiecesMut.ReleaseMutex();

                return result;
            }
        }

        public float CompletedBefore = 0;

        public DownloadTask(RemoteFile[] file, Downloads handler)
        {
            Sources = new List<RemoteFile>(file);
            Connections = new List<TransferConnection>();
            Parent = handler;
            ReceivedPieces = new PieceStatus[file[0].PieceCount()];

            for (int i = 0; i < ReceivedPieces.Length; i++)
                ReceivedPieces[i] = PieceStatus.Missing;
        }

        public void Start()
        {
            DestinationFile = new LocalFile(Parent.IncomingPath() + "\\" + File.Name, File.Length);

            foreach (RemoteFile f in Sources)
            {
                TransferConnection conn = new TransferConnection();

                conn.StartDownload(f.Owner, Parent.LocalAddress, this);

                Connections.Add(conn);
            }
        }

        public void Close()
        {
            ConnectionsMut.WaitOne();

            foreach (TransferConnection conn in Connections)
                conn.Close();

            ConnectionsMut.ReleaseMutex();
        }

        public void TransferEnded(TransferConnection conn)
        {
            ConnectionsMut.WaitOne();

            Connections.Remove(conn);

            ConnectionsMut.ReleaseMutex();
        }

        public int PieceSize(int sequenceNumber)
        {
            return (int)Math.Min(File.Length - LocalFile.PieceSize * sequenceNumber, (long)LocalFile.PieceSize);
        }

        public bool NextPiece(byte[] avaible, out int SequenceNumber)
        {
            PiecesMut.WaitOne();

            for (int i = 0; i < ReceivedPieces.Length; i++)
            {
                if (ReceivedPieces[i] == PieceStatus.Missing &&
                    avaible[i] == 0xFF)
                {
                    SequenceNumber = i;
                    ReceivedPieces[i] = PieceStatus.InProgress;

                    PiecesMut.ReleaseMutex();
                    return true;
                }
            }

            SequenceNumber = -1;

            PiecesMut.ReleaseMutex();
            return false;
        }

        public void DownloadFailed(int sequenceNumber)
        {
            PiecesMut.WaitOne();

            if (ReceivedPieces[sequenceNumber] == PieceStatus.InProgress)
                ReceivedPieces[sequenceNumber] = PieceStatus.Downloaded;

            PiecesMut.ReleaseMutex();
        }

        public void DownloadSucceeded(Piece piece)
        {
            PiecesMut.WaitOne();

            ReceivedPieces[piece.SequenceNumber] = PieceStatus.Downloaded;
            DestinationFile.WritePiece(piece);

            PiecesMut.ReleaseMutex();

            if (Completed == 1.0)
            {
                Parent.DownloadCompleted(this);
            }
        }

        public DownloadItem GetDownloadInfo()
        {
            DownloadItem item = new DownloadItem();

            item.Name = File.Name;
            item.Length = File.Length;
            item.Speed = 0;
            item.Completed = CompletedBefore = Completed;

            ConnectionsMut.WaitOne();

            item.Neighbors = new NeighborItem[Connections.Count];

            int i = 0;
            foreach (TransferConnection conn in Connections)
            {
                NeighborItem nItem = new NeighborItem();
                nItem.Name = conn.ConnectVizinho.Name;
                nItem.Address = conn.ConnectVizinho.IP;
                nItem.Speed = conn.GetSpeed();
                item.Speed += nItem.Speed;

                item.Neighbors[i] = nItem;
                i++;
            }

            ConnectionsMut.ReleaseMutex();

            return item;
        }
    }
}
